import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.*;
import javax.media.opengl.awt.*;  
import javax.media.opengl.glu.*;
import com.jogamp.opengl.util.gl2.GLUT; 

public class FlatShadedSphere  extends glskeleton
                          implements GLEventListener, KeyListener 
                          {

    private GLUT glut;
    
    float rotX = 0;
    float rotY = 0;
    float rotZ = 0;
    
    boolean perspective = false;
    boolean sphere = false;
    
    public static void main(String[] args) {
    
    
        final javax.media.opengl.awt.GLCanvas canvas = new javax.media.opengl.awt.GLCanvas();
        FlatShadedSphere demo = new FlatShadedSphere();
        demo.setCanvas(canvas);
        canvas.addGLEventListener(demo);
        demo.setDefaultListeners(demo);
        
        JFrame frame = new JFrame("Flat Shading by OpenGL");
        frame.setSize(500+8, 500+26+8);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.getContentPane().add(canvas);
        frame.setVisible(true);
        canvas.requestFocusInWindow();
    }
    
    /*
     * Initialize material property, light source, lighting model, and depth
     * buffer.
     */
    public void init(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();
        glut = new GLUT();


        gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        
        gl.glShadeModel(GL2.GL_FLAT);       
        gl.glEnable(GL2.GL_DEPTH_TEST);   
        
        if (true) {
            float[] light_position = { 0f, 0f, 1000.0f, 0.0f };  // at "infinity"
            float[] lightAmbient = {0.25f, 0.25f, 0.25f, 1.0f}; 
            float[] noLight = {0,0,0,1};       
            float[] lightDiffuse = {1f, 1f, 1f, 1f};   
                       
            gl.glEnable(GL2.GL_LIGHTING);
            gl.glEnable(GL2.GL_LIGHT0);

            gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, light_position, 0);
            
            // make sure diffuse light is the only light we're using          
            gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, lightDiffuse, 0);
            gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, noLight, 0);
            gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_SPECULAR, noLight, 0);            
          
        } 
        
    }
    
    public void doSphere(GL2 gl) {
        
        double r = 1.0;
        int stacks = 20;
        int slices = 20;        

        double rhd = (2*Math.PI/slices);
        double thd = (Math.PI/stacks);
        
        // sphere - everything but the north and south poles;  done with trapezoids
        double theta = thd;        
        double rho;
  
        for (int j = 0; j < stacks-2; j++) {
            rho = rhd;
            for (int i = 0; i < slices; i++) {
                
                gl.glBegin(gl.GL_POLYGON);  
 
                gl.glNormal3d(r*Math.sin(theta+thd/2)*Math.cos(rho+rhd/2),
                              r*Math.sin(theta+thd/2)*Math.sin(rho+rhd/2),
                              r*Math.cos(theta+thd/2));
                   
                gl.glVertex3d(r*Math.sin(theta)*Math.cos(rho),
                              r*Math.sin(theta)*Math.sin(rho),
                              r*Math.cos(theta));
                   
                gl.glVertex3d(r*Math.sin(theta)*Math.cos(rho+rhd),
                              r*Math.sin(theta)*Math.sin(rho+rhd),
                              r*Math.cos(theta));
                   
                gl.glVertex3d(r*Math.sin(theta+thd)*Math.cos(rho+rhd),
                              r*Math.sin(theta+thd)*Math.sin(rho+rhd),
                              r*Math.cos(theta+thd));
                        
                gl.glVertex3d(r*Math.sin(theta+thd)*Math.cos(rho),
                              r*Math.sin(theta+thd)*Math.sin(rho),
                              r*Math.cos(theta+thd));                
                   
                gl.glEnd();
                
                  
                rho += rhd;                
            }               
            theta += thd;
        }
        
        // first of two polar regions; done with triangles;
        theta = Math.PI - thd;
        rho = 0;
        for (int i = 0; i < slices; i++) {
                gl.glBegin(gl.GL_POLYGON);  
                   
                gl.glNormal3d(r*Math.sin(theta+thd/2)*Math.cos(rho+rhd/2),
                              r*Math.sin(theta+thd/2)*Math.sin(rho+rhd/2),
                              r*Math.cos(theta+thd/2));
                   
                gl.glVertex3d(r*Math.sin(theta)*Math.cos(rho),
                              r*Math.sin(theta)*Math.sin(rho),
                              r*Math.cos(theta));
                   
                gl.glVertex3d(r*Math.sin(theta)*Math.cos(rho+rhd),
                              r*Math.sin(theta)*Math.sin(rho+rhd),
                              r*Math.cos(theta));
                        
                gl.glVertex3d(r*Math.sin(theta+thd)*Math.cos(rho+rhd),
                              r*Math.sin(theta+thd)*Math.sin(rho+rhd),
                              r*Math.cos(theta+thd));
                          
                   
                gl.glEnd();
                                  
                rho += rhd;        
              
        }
        
        
        // second of two polar regions; done with triangles;        
        theta = 0;
        rho = 0;
        for (int i = 0; i < slices; i++) {
                gl.glBegin(gl.GL_POLYGON);  
                   
                gl.glNormal3d(r*Math.sin(theta+thd/2)*Math.cos(rho+rhd/2),
                              r*Math.sin(theta+thd/2)*Math.sin(rho+rhd/2),
                              r*Math.cos(theta+thd/2));
                   
                gl.glVertex3d(r*Math.sin(theta)*Math.cos(rho),
                              r*Math.sin(theta)*Math.sin(rho),
                              r*Math.cos(theta));
                        
                gl.glVertex3d(r*Math.sin(theta+thd)*Math.cos(rho+rhd),
                              r*Math.sin(theta+thd)*Math.sin(rho+rhd),
                              r*Math.cos(theta+thd));
                        
                gl.glVertex3d(r*Math.sin(theta+thd)*Math.cos(rho),
                              r*Math.sin(theta+thd)*Math.sin(rho),
                              r*Math.cos(theta+thd));                               
                   
                gl.glEnd();
                  
                rho += rhd;        
              
        }
        
        
    
    
    }

    
    public void display(GLAutoDrawable drawable) {

        GL2 gl = drawable.getGL().getGL2();
        
        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
        gl.glColor3f(1,0,0);
        
        // only diffuse light in use, so
        // only diffuse material color specified
        float mat_diffuse[] = { 0.5f, 1.0f, 0.5f, 1.0f };  // greenish   
        gl.glMaterialfv(GL.GL_FRONT, GL2.GL_DIFFUSE, mat_diffuse, 0);

        gl.glLoadIdentity();  
        
        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();       
        if (perspective)
            gl.glFrustum(-0.65,0.65,-0.65,0.65,1,4.5);
        else
            gl.glOrtho(-1.5, 1.5, -1.5, 1.5, -10.0, 10.0);
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        
        
        gl.glTranslatef(0,0,-2.5f); 

        gl.glRotatef(rotX,1,0,0);
        gl.glRotatef(rotY,0,1,0);
        gl.glRotatef(rotZ,0,0,1);           

        if (sphere) {
           doSphere(gl);
        } else {
           glut.glutSolidCube(1.0f);
        }
        
        gl.glFlush();
    }
    
    public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glViewport(0, 0, Math.min(w,h), Math.min(w,h)); 
    }
    
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
                               boolean deviceChanged) {
    }
    
    public void keyTyped(KeyEvent key) {
    }
    
    public void dispose(javax.media.opengl.GLAutoDrawable dr) {
    }        
    
    public void keyPressed(KeyEvent key) {
        switch (key.getKeyCode()) {
            case KeyEvent.VK_P:
                perspective = true;
                refresh();
                break;
            case KeyEvent.VK_N:
                perspective = false;
                refresh();
                break;  
            case KeyEvent.VK_S:
                sphere = true;
                refresh();
                break;
            case KeyEvent.VK_C:
                sphere = false;
                refresh();
                break;                          
            case KeyEvent.VK_ESCAPE:
                super.runExit();
                break;
            case KeyEvent.VK_LEFT:
                rotY -= 3;
                refresh();
                break;  
            case KeyEvent.VK_RIGHT:
                rotY += 3;
                refresh();
                break;  
            case KeyEvent.VK_UP:
                rotX -= 3;
                refresh();              
                break;  
            case KeyEvent.VK_DOWN:
                rotX += 3;
                refresh();
                break;  
            case KeyEvent.VK_PAGE_UP:
                rotZ += 3;
                refresh();
                break;
            case KeyEvent.VK_PAGE_DOWN:
                rotZ -= 3;
                refresh();
                break;                                                                                             
            default:
                System.out.println(key.getKeyCode());
                break;
        }
    }
    
    public void keyReleased(KeyEvent key) {
    }
    
}
